// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.api.ads.dfp.axis;

import com.google.api.ads.common.lib.auth.ClientLoginTokens;
import com.google.api.ads.common.lib.conf.ConfigurationHelper;
import com.google.api.ads.common.lib.conf.ConfigurationLoadException;
import com.google.api.ads.common.lib.exception.ValidationException;
import com.google.api.ads.dfp.axis.factory.DfpServices;
import com.google.api.ads.dfp.axis.v201203.AdUnit;
import com.google.api.ads.dfp.axis.v201203.AdUnitSize;
import com.google.api.ads.dfp.axis.v201203.AdUnitTargetWindow;
import com.google.api.ads.dfp.axis.v201203.Company;
import com.google.api.ads.dfp.axis.v201203.CompanyServiceInterface;
import com.google.api.ads.dfp.axis.v201203.CompanyType;
import com.google.api.ads.dfp.axis.v201203.EnvironmentType;
import com.google.api.ads.dfp.axis.v201203.InventoryServiceInterface;
import com.google.api.ads.dfp.axis.v201203.NetworkServiceInterface;
import com.google.api.ads.dfp.axis.v201203.Size;
import com.google.api.ads.dfp.axis.v201203.TargetPlatform;
import com.google.api.ads.dfp.axis.v201211.CreativeWrapper;
import com.google.api.ads.dfp.axis.v201211.CreativeWrapperHtmlSnippet;
import com.google.api.ads.dfp.axis.v201211.CreativeWrapperOrdering;
import com.google.api.ads.dfp.axis.v201211.CreativeWrapperServiceInterface;
import com.google.api.ads.dfp.axis.v201211.Label;
import com.google.api.ads.dfp.axis.v201211.LabelServiceInterface;
import com.google.api.ads.dfp.axis.v201211.LabelType;
import com.google.api.ads.dfp.lib.client.DfpSession;

import org.apache.commons.configuration.Configuration;

import java.io.IOException;
import java.rmi.RemoteException;
import java.util.Random;

/**
 * Helper class for DFP Axis examples. Provides functionality to lazily
 * create prerequisite objects for testing.
 *
 * @author Adam Rogal
 */
public class DfpAxisTestHelper {

  private String authToken;
  private Configuration config;
  private DfpServices dfpServices;
  private String effectiveRootAdUnitId;
  private Long testAdvertiserId;
  private String testAdUnitId;
  private Long testCreativeWrapperId;
  private Long testCreativeWrapperLabelId;

  /**
   * Instantiates this helper class and obtains an AuthToken (which is cached).
   */
  public DfpAxisTestHelper() {
    try {
      this.authToken =
          new ClientLoginTokens.Builder()
              .forApi(ClientLoginTokens.Api.DFP)
              .fromFile()
              .build()
              .requestToken();
    } catch (ValidationException e) {
      throw new IllegalStateException("Could not obtain an AuthToken.", e);
    } catch (IOException e) {
      throw new IllegalStateException("Could not obtain an AuthToken.", e);
    } catch (ConfigurationLoadException e) {
      throw new IllegalStateException("Could not obtain an AuthToken.", e);
    }
  }

  /**
   * Gets the configuration used to create the {@link DfpSession}.
   */
  public Configuration getConfig() {
    if (config == null) {
      try {
        config = new ConfigurationHelper().fromFile("ads.properties");
      } catch (ConfigurationLoadException e) {
        throw new IllegalStateException("Could not obtain an AuthToken.", e);
      }
    }
    return config;
  }

  /**
   * Gets a brand-new DfpSession (but with re-used authToken).
   */
  public DfpSession getSession() {
    try {
      return new DfpSession.Builder().from(getConfig()).withClientLoginToken(authToken).build();
    } catch (ValidationException e) {
      throw new IllegalStateException("Could not build a session.", e);
    }
  }

  /**
   * Gets a memoized DFP services object.
   * 
   * @throws ValidationException if DfpServices cannot be constructed
   */
  public synchronized DfpServices getDfpServices() throws ValidationException {
    if (dfpServices == null) {
      dfpServices = new DfpServices();
    }
    return dfpServices;
  }

  /**
   * Gets a test advertiser ID.
   *
   * @param replace {@code true} if a new company should be created
   * @throws ValidationException if the service could not be made
   * @throws RemoteException if there was an error making the API call
   */
  public synchronized Long getTestAdvertiserId(boolean replace) throws ValidationException,
      RemoteException {
    if (testAdvertiserId == null || replace) {
      // Get the CompanyService.
      CompanyServiceInterface companyService =
          getDfpServices().get(getSession(), CompanyServiceInterface.class);

      // Create an advertiser.
      Company company = new Company();
      company.setName("Advertiser #" + new Random().nextLong());
      company.setType(CompanyType.ADVERTISER);

      // Create the companies on the server.
      Company[] companies = companyService.createCompanies(new Company[] {company});

      testAdvertiserId = companies[0].getId();
    }
    return testAdvertiserId;
  }

  /**
   * Gets the effective root ad unit ID.
   *
   * @throws ValidationException if the service could not be made
   * @throws RemoteException if there was an error making the API call
   */
  public synchronized String getEffectiveRootAdUnitId()
      throws ValidationException, RemoteException {
    if (effectiveRootAdUnitId == null) {
      // Get the NetworkService.
      NetworkServiceInterface networkService =
          getDfpServices().get(getSession(), NetworkServiceInterface.class);

      // Get the InventoryService.
      InventoryServiceInterface inventoryService =
          getDfpServices().get(getSession(), InventoryServiceInterface.class);

      // Get the effective root ad unit.
      effectiveRootAdUnitId = inventoryService
          .getAdUnit(networkService.getCurrentNetwork().getEffectiveRootAdUnitId()).getId();
    }
    return effectiveRootAdUnitId;
  }

  /**
   * Gets a test advertiser ID.
   *
   * @param replace {@code true} if a new ad unit should be created
   * @throws ValidationException if the service could not be made
   * @throws RemoteException if there was an error making the API call
   */
  public synchronized String getTestAdUnitId(boolean replace) throws ValidationException,
      RemoteException {
    if (testAdUnitId == null || replace) {
      // Get the InventoryService.
      InventoryServiceInterface inventoryService =
          getDfpServices().get(getSession(), InventoryServiceInterface.class);

      // Create ad unit size.
      Size size = new Size();
      size.setWidth(300);
      size.setHeight(250);
      size.setIsAspectRatio(false);

      AdUnitSize adUnitSize = new AdUnitSize();
      adUnitSize.setSize(size);
      adUnitSize.setEnvironmentType(EnvironmentType.BROWSER);

      // Create a web ad unit.
      AdUnit adUnit = new AdUnit();
      adUnit.setName("Ad_unit_" + new Random().nextLong());
      adUnit.setDescription(adUnit.getName());
      adUnit.setTargetPlatform(TargetPlatform.WEB);
      adUnit.setParentId(getEffectiveRootAdUnitId());
      adUnit.setTargetWindow(AdUnitTargetWindow.BLANK);
      adUnit.setAdUnitSizes(new AdUnitSize[]{adUnitSize});

      // Create the ad units on the server.
      AdUnit[] adUnits = inventoryService.createAdUnits(new AdUnit[] {adUnit});

      testAdUnitId = adUnits[0].getId();
    }
    return testAdUnitId;
  }

  /**
   * Gets a test creative wrapper label ID.
   *
   * @param replace {@code true} if a new creative wrapper label should be created
   * @throws ValidationException if the service could not be made
   * @throws RemoteException if there was an error making the API call
   */
  public synchronized long getTestCreativeWrapperLabelId(boolean replace)
      throws ValidationException, RemoteException {
    if (testCreativeWrapperLabelId == null || replace) {
      // Get the LabelService.
      LabelServiceInterface labelService =
          getDfpServices().get(getSession(), LabelServiceInterface.class);
      
      // Create a creative wrapper.
      Label creativeWrapperLabel = new Label();
      creativeWrapperLabel.setName("Label #" + new Random().nextLong());
      creativeWrapperLabel.setTypes(new LabelType[] {LabelType.CREATIVE_WRAPPER});
  
      // Create the creative wrappers on the server.
      Label[] labels =
          labelService.createLabels(new Label[] {creativeWrapperLabel});
      
      testCreativeWrapperLabelId = labels[0].getId();
    }
    return testCreativeWrapperLabelId;
  }

  /**
   * Gets a test creative wrapper ID.
   *
   * @param replace {@code true} if a new creative wrapper should be created
   * @throws ValidationException if the service could not be made
   * @throws RemoteException if there was an error making the API call
   */
  public synchronized Long getTestCreativeWrapperId(boolean replace)
      throws ValidationException, RemoteException {
    return getTestCreativeWrapperId(replace, getTestCreativeWrapperLabelId(replace));
  }
  
  /**
   * Gets a test creative wrapper ID.
   *
   * @param replace {@code true} if a new creative wrapper should be created
   * @param labelId the label to associate the creative wrapper with
   * @throws ValidationException if the service could not be made
   * @throws RemoteException if there was an error making the API call
   */
  public synchronized Long getTestCreativeWrapperId(boolean replace, Long labelId)
      throws ValidationException, RemoteException {    
    if (testCreativeWrapperId == null || replace) {
      // Get the CreativeWrapperService.
      CreativeWrapperServiceInterface creativeWrapperService =
          getDfpServices().get(getSession(), CreativeWrapperServiceInterface.class);
      
      // Create a creative wrapper.
      CreativeWrapper innerCreativeWrapper = new CreativeWrapper();
      // A label can only be associated with one creative wrapper.
      innerCreativeWrapper.setLabelId(labelId);
      innerCreativeWrapper.setOrdering(CreativeWrapperOrdering.INNER);
      innerCreativeWrapper.setHeader(
          new CreativeWrapperHtmlSnippet("<b>My creative wrapper header</b>"));
      innerCreativeWrapper.setFooter(
          new CreativeWrapperHtmlSnippet("<b>My creative wrapper footer</b>"));
  
      // Create the creative wrappers on the server.
      CreativeWrapper[] creativeWrappers = creativeWrapperService.createCreativeWrappers(
          new CreativeWrapper[] {innerCreativeWrapper});
      
      testCreativeWrapperId = creativeWrappers[0].getId();
    }
    return testCreativeWrapperId;
  }
}
